
/*
 * Copyright (c) 2010 Sergey N Frolov
 * 
 * See LICENSE file
 */

#ifndef _NGX_JAVASCRIPT_H_INCLUDED_
#define _NGX_JAVASCRIPT_H_INCLUDED_

#ifndef NGX_JAVASCRIPT_PREFIX
#define NGX_JAVASCRIPT_PREFIX "/usr/local/nginx"
#endif

#define _REENTRANT
#define _GNU_SOURCE

#include <event.h>
#define XP_UNIX
#include <jsapi.h>
#include <jshash.h>
#include <jsdate.h>
#include <jsxdrapi.h>
#include <jsdbgapi.h>

#include <sys/un.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>

#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <pthread.h>

#ifndef min
#define min(a,b) ((a) < (b) ? (a) : (b))
#endif

#ifndef max
#define max(a,b) ((a) > (b) ? (a) : (b))
#endif

typedef struct ngx_javascript_server_s ngx_javascript_server_t;
typedef struct ngx_javascript_client_s ngx_javascript_client_t;
typedef struct ngx_javascript_worker_s ngx_javascript_worker_t;
typedef struct ngx_javascript_buffer_s ngx_javascript_buffer_t;
typedef struct ngx_javascript_memory_s ngx_javascript_memory_t;
typedef struct ngx_javascript_stat_s ngx_javascript_stat_t;
typedef struct ngx_javascript_multi_part_s ngx_javascript_multi_part_t;

#include "ngx_javascript_fcgi.h"

enum ngx_javascript_client_status_e {
	NGX_JAVASCRIPT_STATUS_READ = 0,
	NGX_JAVASCRIPT_STATUS_BEGIN_REQUEST,
	NGX_JAVASCRIPT_STATUS_ABORT_REQUEST,
	NGX_JAVASCRIPT_STATUS_GET_VALUES,
	NGX_JAVASCRIPT_STATUS_PARAMS,
	NGX_JAVASCRIPT_STATUS_STDIN,
	NGX_JAVASCRIPT_STATUS_DATA,
	NGX_JAVASCRIPT_STATUS_EXEC,
	NGX_JAVASCRIPT_STATUS_WRITE
};

struct ngx_javascript_server_s {
	int fd;                           // Listening socket descriptor
	int fg;                           // Run in foreground
	uid_t uid;                        // UID
	gid_t gid;                        // GID
	pid_t pid;                        // Server pid
	char *program;                    // Program name
	pthread_mutex_t lock;             // Lock
	ngx_javascript_client_t **clients;// Clients table
	ngx_javascript_worker_t **workers;// Workers table
	int clients_max;                  // Maximum number of clients
	int clients_len;                  // Current number of allocated client slots
	int workers_max;                  // Maximum number of workers
	int workers_len;                  // Current number of allocated workers
	struct event accept_event;        // Accept event
	struct event sigint_event;        // SIGINT event
	char *socket_path;                // Path to socket
	mode_t socket_mode;               // Socket mode
	unsigned int pagesize;            // Page size (4K or 8K)
	int timeout;                      // Timeout of execution
	char *pid_file;                   // PID file
	char *log_file;                   // Log file
	char *temp_path;                  // Temporary path
	size_t max_form;                  // Maximum application/x-www-form-urlencoded
	size_t max_data;                  // Maximum multipart/form-data
	size_t stack_quota;               // Stack quota
	JSRuntime *jsr;                   // JavaScript runtime
};

struct ngx_javascript_client_s {
	int n;                            // Number of this client
	int fd;                           // Socket descriptor
	int id;                           // FastCGI request id
	int keep;                         // Keep alieve
	int wait;                         // Read / Write event is active
	int status;                       // Current status
	struct event event;               // Read / Write event
	ngx_javascript_server_t *server;  // Main server
	ngx_javascript_worker_t *worker;  // Worker
	
	ngx_javascript_memory_t *memory;  // Pointer to memory allocated for client
	ngx_javascript_memory_t *endmem;  // Pointer to last allocated memory
	
	char *input;                      // Pointer to input buffer. Size is max(next_len + next_pad, getpagesize())
	int input_dat;                    // Length of data allocated for input
	int input_len;                    // Length of data we want to read
	int input_pad;                    // Length of pading we want to read
	int input_pos;                    // Number of bytes read into input buffer
	
	char *body;                       // Full request body
	int body_fd;                      // Request body fd
	size_t body_pos;                  // Current body position
	int body_type;                    // Type of body
	
	ngx_javascript_buffer_t *output;  // Pointer to output buffer chain
	ngx_javascript_buffer_t *endout;  // Pointer to the last output buffer in the chain
	int position;                     // Position of write in the current buffer
	
	time_t start;                     // Start of Javascript interpreter
	pthread_t thread;                 // Thread associated with this client
	JSHashTable *env;                 // Environment variables
	
	/* javascript */
	size_t content_length;            // Content length
	char *content_type;               // Content type
	unsigned int content_type_len;    // Content type length
	char *document_root;              // Document root
	unsigned int document_root_len;   // Document root length
	char *script_filename;            // Script filename
	unsigned int script_filename_len; // Script filename length
	char *open_basedir;               // Open basedir
	unsigned int open_basedir_len;    // Open basedir length
	char *request_uri;                // Request URI
	unsigned int request_uri_len;     // Request URI length
	char *query_string;               // Query string
	unsigned int query_string_len;    // Query string length
	char *cookie;                     // Cookie
	unsigned int cookie_len;          // Cookie length
};

struct ngx_javascript_worker_s {
	JSContext *jcx;                   // Javascript context
	ngx_javascript_server_t *server;  // Server
	ngx_javascript_client_t *client;  // Client
	
	JSHashTable *stat_cache;          // Stat cache
	JSHashTable *link_cache;          // Readlink cache
	
	ngx_javascript_stat_t *stat_data; // Pointer to next stat data
	unsigned int stat_have;           // Number of stat data remained
	
	char *link_data;                  // Pointer to next readlink data
	unsigned int link_have;           // Number of bytes for readlink data remained
	
	ngx_javascript_memory_t *memory;  // Pointer to memory allocated for worker
	ngx_javascript_memory_t *endmem;  // Pointer to last allocated memory
};

struct ngx_javascript_buffer_s {
	ngx_javascript_buffer_t *next;    // Next buffer
	int len;                          // Length of buffer
	fcgi_header head;                 // FastCGI header
	char data[];                      // Buffer data
};

struct ngx_javascript_memory_s {
	ngx_javascript_memory_t *next;    // Next allocated memory
	char data[];                      // Data
};

struct ngx_javascript_stat_s {
	int no;                           // errno
	struct stat st;                   // struct stat
};

struct ngx_javascript_multi_part_s {
	ngx_javascript_client_t *client;  // Client
	int fd;                           // File descriptor
	size_t length;                    // Length of whole file
	size_t offset;                    // Current read offset
	char *boundary;                   // Boundary as string
	size_t boundary_len;              // Boundary length
	char *b;                          // Read buffer
	size_t l;                         // Buffer length
	size_t o;                         // Buffer offset in file
	char *h;                          // Current header
	size_t hl;                        // Current header length
	size_t ho;                        // Current header offset
	size_t rn;                        // end-of-line length (1 when \n, 2 when \r\n)
};

#define NGX_JAVASCRIPT_BUFFER 4096

#define NGX_UNESCAPE_URI 1
#define NGX_UNESCAPE_REDIRECT 2

#define NGX_JAVASCRIPT_PARSE_POST 0
#define NGX_JAVASCRIPT_PARSE_GET 1
#define NGX_JAVASCRIPT_PARSE_COOKIE 2
#define NGX_JAVASCRIPT_PARSE_STRING 3
#define NGX_JAVASCRIPT_PARSE_ENV 4
#define NGX_JAVASCRIPT_PARSE_SERVER 5
#define NGX_JAVASCRIPT_PARSE_SESSION 6

#define NGX_JAVASCRIPT_PARSE_FORM 1
#define NGX_JAVASCRIPT_PARSE_DATA 2

#define NGX_JAVASCRIPT_BUFFER 4096
#define NGX_JAVASCRIPT_BUFFER1 1024
#define NGX_JAVASCRIPT_BUFFER2 8192

#ifdef NGX_JAVASCRIPT_HAVE_FILE
extern JSBool ngx_javascript_init_file(JSContext *jcx, JSObject *global);
#endif /* NGX_JAVASCRIPT_HAVE_FILE */

#ifdef NGX_JAVASCRIPT_HAVE_MYSQL
extern JSBool ngx_javascript_init_mysql(JSContext *jcx, JSObject *global);
#endif /* NGX_JAVASCRIPT_HAVE_MYSQL */

#ifdef NGX_JAVASCRIPT_HAVE_SQLITE
extern JSBool ngx_javascript_init_sqlite(JSContext *jcx, JSObject *global);
#endif /* NGX_JAVASCRIPT_HAVE_SQLITE */

# endif

/* headers */
/usr/bin/perl
/bin/grep
/bin/sed

ngx_javascript_client_t *ngx_javascript_server_client_slot(ngx_javascript_server_t *server);
int ngx_javascript_client_read(ngx_javascript_client_t *client, char *buf, int len);
int ngx_javascript_client_write(ngx_javascript_client_t *client, char *buf, int len);
int ngx_javascript_client_socket_nonblock(int s);
void ngx_javascript_client_free(ngx_javascript_client_t *client);
void ngx_javascript_client_close(ngx_javascript_client_t *client);
void *ngx_javascript_client_allocate(ngx_javascript_client_t *client, int size);
void ngx_javascript_client_write_event(int fd, short event, void *arg);
void *ngx_javascript_client_execute_thread(void *arg);
int ngx_javascript_client_execute(ngx_javascript_client_t *client);
void ngx_javascript_client_read_event(int fd, short event, void *arg);
int ngx_javascript_client_body(ngx_javascript_client_t *client, int op);
size_t ngx_javascript_fcgi_atos(unsigned char *line, size_t n);
int ngx_javascript_fcgi_read_header(ngx_javascript_client_t *client, fcgi_header *hdr);
int ngx_javascript_fcgi_next_status(ngx_javascript_client_t *client, int status, int len, int pad);
int ngx_javascript_fcgi_get_params(ngx_javascript_client_t *client);
int ngx_javascript_fcgi_read_request(ngx_javascript_client_t *client);
int ngx_javascript_fcgi_padding(int len);
int ngx_javascript_fcgi_make_header(fcgi_header *hdr, fcgi_request_type type, int req_id, int len, int pad);
int ngx_javascript_fcgi_write_responce(ngx_javascript_client_t *client);
intN ngx_javascript_hash_remove_entry(JSHashEntry *hash, intN i, void *arg);
intN ngx_javascript_hash_strcmp(const void *v1, const void *v2);
int ngx_javascript_main_usage(ngx_javascript_server_t *server);
int ngx_javascript_main_options(ngx_javascript_server_t *server, int argc, char *argv[]);
int ngx_javascript_main_running(ngx_javascript_server_t *server);
int ngx_javascript_main_daemonize(ngx_javascript_server_t *server);
void ngx_javascript_main_signal(int signal);
int ngx_javascript_main_start(ngx_javascript_server_t *server);
int ngx_javascript_main_run(ngx_javascript_server_t *server);
int main(int argc, char *argv[]);
void *ngx_javascript_calloc(size_t size);
void *ngx_javascript_malloc(size_t size);
void ngx_javascript_free(void *ptr);
void *ngx_javascript_realloc(void *ptr, size_t size);
void *JS_calloc(JSContext *jcx, size_t size);
void ngx_javascript_timer(int report);
size_t ngx_javascript_strlcpy(char *dst, const char *src, size_t siz);
size_t ngx_javascript_strlcat(char *dst, const char *src, size_t siz);
char *ngx_javascript_realpath(ngx_javascript_worker_t *worker, const char *path, char resolved[PATH_MAX]);
int ngx_javascript_lstat(ngx_javascript_worker_t *worker, const char *path, struct stat *buf);
int ngx_javascript_readlink(ngx_javascript_worker_t *worker, const char *path, char *buf, size_t bufsize);
JSBool ngx_javascript_fullpath(JSContext *jcx, char *filename, char *fullname);
JSBool ngx_javascript_check_basedir(JSContext *jcx, char *filename, int mode);
JSBool ngx_javascript_check_basedir_realpath(JSContext *jcx, const char *filename);
size_t strlens(const char *str);
JSBool ngx_javascript_read_request(JSContext *jcx, JSObject *obj, int arg);
JSBool ngx_javascript_register_variable(JSContext *jcx, JSObject *obj, char *var, size_t var_len, char *val, size_t val_len);
JSBool ngx_javascript_register_variable_r(JSContext *jcx, JSObject *obj, char *var, size_t var_len, char *val, size_t val_len);
char *ngx_javascript_string_token(char *s, char *delim, char **last);
void ngx_unescape_uri(char **dst, char **src, size_t size, unsigned int type);
JSBool ngx_javascript_read_form(JSContext *jcx, JSObject *obj, ngx_javascript_client_t *client);
JSBool ngx_javascript_get_boundary(ngx_javascript_client_t *client, char **boundary_data, size_t *boundary_length);
size_t ngx_javascript_read_from_file(ngx_javascript_multi_part_t *m, size_t s);
JSBool ngx_javascript_read_line_file(ngx_javascript_multi_part_t *m, size_t s, size_t *ro, char **rp);
JSBool ngx_javascript_read_boundary_file(ngx_javascript_multi_part_t *m, size_t s, size_t *ro, char **rp);
JSBool ngx_javascript_read_data_file(JSContext *jcx, JSObject *obj, ngx_javascript_client_t *client);
JSBool ngx_javascript_multipart_headers(ngx_javascript_multi_part_t *m, char **rvar, size_t *rvar_len, char **rname, size_t *rname_len);
char *ngx_javascript_read_line(char *s, char *e);
JSBool ngx_javascript_read_boundary(ngx_javascript_multi_part_t *m, char *s, char **rp);
JSBool ngx_javascript_read_data(JSContext *jcx, JSObject *obj, ngx_javascript_client_t *client);
char *ngx_strlcasestrn(char *s1, char *last, char *s2, size_t n);
int ngx_strncasecmp(char *s1, char *s2, size_t n);
JSBool ngx_javascript_execute_script(JSContext *jcx, JSObject *obj, const char *fileName, jsval *rval);
JSBool ngx_javascript_global_load(JSContext *jcx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
int ngx_javascript_server_socket_nonblock(int s);
int ngx_javascript_server_socket(ngx_javascript_server_t *server);
void ngx_javascript_server_accept_event(int fd, short event, void *arg);
void ngx_javascript_server_shutdown(ngx_javascript_server_t *server);
void ngx_javascript_server_signal_event(int fd, short event, void *arg);
ngx_javascript_worker_t *ngx_javascript_server_worker_slot(ngx_javascript_server_t *server);
void ngx_javascript_worker_close(ngx_javascript_worker_t *worker);
void ngx_javascript_worker_free(ngx_javascript_worker_t *worker);
JSBool ngx_javascript_server_resolve(JSContext *jcx, JSObject *obj, jsval idval);
JSBool ngx_javascript_request_resolve(JSContext *jcx, JSObject *obj, jsval idval);
int ngx_javascript_worker_init(ngx_javascript_worker_t *worker);
int ngx_javascript_worker_execute(ngx_javascript_worker_t *worker, int *rstatus, char **routput, size_t *routput_len, char **rheader, size_t *rheader_len);
JSBool ngx_javascript_global_file_line(JSContext *jcx, const char **file, int *line);
JSBool ngx_javascript_global_file_string(JSContext *jcx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool ngx_javascript_global_line_string(JSContext *jcx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool ngx_javascript_timeout(JSContext *jcx);
JSBool ngx_javascript_outofmemory(JSContext *jcx);
void ngx_javascript_global_error(JSContext *jcx, const char *message, JSErrorReport *report);
void ngx_javascript_log(JSContext *jcx, const char *message);
JSBool ngx_javascript_global_alert(JSContext *jcx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool ngx_javascript_global_print(JSContext *jcx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool ngx_javascript_global_sleep(JSContext *jcx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool ngx_javascript_global_usleep(JSContext *jcx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool ngx_javascript_global_header_toString(JSContext *jcx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool ngx_javascript_private_error(JSContext *jcx);
JSBool ngx_javascript_error(JSContext *jcx, const char *message);
void *ngx_javascript_worker_allocate(ngx_javascript_worker_t *worker, int size);
